//go:build linux
// +build linux

package main

import (
	// "errors"
	"encoding/base64"
	"io/fs"
	"os"
	"syscall"

	// syscall "golang.org/x/sys/unix"
	"encoding/json"
	"time"

	log "github.com/sirupsen/logrus"
)

func (fuser *Fuse) Access(path string, mode int) bool {
	// fuse.access()
	full_path := Full_path(path)
	// log.Printf("%v %v", full_path, mode)
	// fp,err := os.OpenFile(full_path, mode, 0644)
	// defer fp.Close()

	if err := syscall.Access(full_path, uint32(mode)); err != nil {
		return false
	}
	return true
	// fi, err := os.Stat(full_path)
	// if err != nil {
	// 	log.Printf("%v %v", full_path, err)
	// 	return false
	// }
	// if int(fi.Mode())&mode == mode {
	// 	// log.Printf("获得权限 %v", full_path)
	// 	return true
	// }
	// // log.Printf("失败 %v", full_path)
	// return false
}

func (fuser *Fuse) Chmod(path string, mode int) interface{} {
	//  fuse.chmod()
	full_path := Full_path(path)
	err := os.Chmod(full_path, os.FileMode(mode))
	// log.Printf("%v %v %v", full_path, mode, err)
	if err != nil {
		// log.Printf("%v %v", full_path, err)
		// return false
		// e, _ := err.(*os.PathError)
		// syscallErr, _ := errors.Unwrap(err).(*os.SyscallError)
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Chown(path string, uid int, gid int) bool {
	//  fuse.chown()
	full_path := Full_path(path)
	err := os.Chown(full_path, uid, gid)
	// log.Printf("%v %v %v %v", full_path, uid, gid, err)
	if err != nil {
		return false
	}
	return true
}

func (fuser *Fuse) Getattr(path string, fd int) dict {
	//  fuse.getattr()
	full_path := Full_path(path)
	var sys syscall.Stat_t
	err := syscall.Stat(full_path, &sys)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	return dict{
		"st_mode":  sys.Mode,
		"st_ino":   sys.Ino,
		"st_dev":   sys.Dev,
		"st_nlink": sys.Nlink,
		"st_uid":   sys.Uid,
		"st_gid":   sys.Gid,
		"st_size":  sys.Size,
		"st_atime": sys.Atim.Sec,
		"st_mtime": sys.Mtim.Sec,
		"st_ctime": sys.Ctim.Sec,
	}
}

func (fuser *Fuse) Readdir(path string, fd int) interface{} {
	//  fuse.readdir()
	full_path := Full_path(path)
	files, err := os.ReadDir(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	var outs []string
	outs = append(outs, ".")
	outs = append(outs, "..")
	for _, file := range files {
		outs = append(outs, file.Name())
	}
	return outs
}

func (fuser *Fuse) Readlink(path string) interface{} {
	//  fuse.readlink()
	full_path := Full_path(path)
	pathname, err := os.Readlink(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	// if pathname.startswith("/"):
	// 	return os.path.relpath(pathname, FUSE.ROOT)
	// else:
	// 	return pathname
	return pathname
}

func (fuser *Fuse) Mknod(path string, mode int, dev int) interface{} {
	//  fuse.mknod()
	ret := syscall.Mknod(Full_path(path), uint32(mode), dev)
	return ret
}

func (fuser *Fuse) Rmdir(path string) interface{} {
	//  fuse.rmdir()
	full_path := Full_path(path)
	err := os.Remove(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			// return OSError(2, "No such file or directory", path)
			// 不存在代表删除成功了
			return true
		}
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Mkdir(path string, mode int) interface{} {
	//  fuse.mkdir()
	err := os.Mkdir(Full_path(path), fs.FileMode(mode))
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Statfs(path string) interface{} {
	//  fuse.statfs()
	full_path := Full_path(path)
	stv := syscall.Statfs_t{}
	err := syscall.Statfs(full_path, &stv)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	// log.Printf("%v %+v", full_path, stv)
	return dict{
		"f_bavail":  stv.Bavail,
		"f_bfree":   stv.Bfree,
		"f_blocks":  stv.Blocks,
		"f_bsize":   stv.Bsize,
		"f_favail":  stv.Ffree,
		"f_ffree":   stv.Ffree,
		"f_files":   stv.Files,
		"f_flag":    stv.Flags,
		"f_frsize":  stv.Frsize,
		"f_namemax": stv.Namelen,
	}
}

func (fuser *Fuse) Unlink(path string) interface{} {
	return fuser.Rmdir(path)
}

func (fuser *Fuse) Symlink(name string, target string) interface{} {
	//  fuse.symlink()
	err := os.Symlink(target, name)
	if err != nil {
		return OSError(1, err.Error(), name)
	}
	return true
}

func (fuser *Fuse) Rename(old string, new string) interface{} {
	//  fuse.rename()
	err := os.Rename(Full_path(old), Full_path(new))
	if err != nil {
		return OSError(1, err.Error(), new)
	}
	return true
}

func (fuser *Fuse) Link(target string, name string) interface{} {
	//  fuse.link()
	err := os.Link(Full_path(target), Full_path(name))
	if err != nil {
		return OSError(1, err.Error(), name)
	}
	return true
}

func (fuser *Fuse) Utimens(path string, timestamps []float64) interface{} {
	//  fuse.utimens()
	var times []time.Time
	for _, ts := range timestamps {
		times = append(times, time.Unix(int64(ts), 0))
	}
	err := os.Chtimes(Full_path(path), times[0], times[1])
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	return true
}

// func (fuser *Fuse) Open(path string, flags int) interface{} {
// 	//  fuse.open()
// 	full_path := Full_path(path)
// 	fd, err := syscall.Open(full_path, flags, syscall.S_IRUSR|syscall.S_IWUSR)
// 	if err != nil {
// 		return OSError(1, err.Error(), path)
// 	}
// 	// defer file.Close()
// 	// defer syscall.Close(fd)

// 	// buf := make([]byte, 10)
// 	// _,_ = syscall.Read(fd, buf)
// 	// log.Printf("Open %v %+v %s", full_path, fd, buf)

// 	return int(fd)
// }

// func (fuser *Fuse) Create(path string, mode int, fd int) interface{} {
// 	//  fuse.create()
// 	full_path := Full_path(path)
// 	fd, err := syscall.Open(full_path, 64|syscall.O_WRONLY, uint32(mode))
// 	if err != nil {
// 		return OSError(1, err.Error(), path)
// 	}
// 	// defer syscall.Close(fd)
// 	return int(fd)
// }

// func (fuser *Fuse) Read(path string, length int, offset int, fd int) interface{} {
// 	//  fuse.read()
// 	// file := Fd2File(fd)
// 	syscall.Seek(fd, int64(offset), os.SEEK_SET)
// 	buffer := make([]byte, length)
// 	_, err := syscall.Read(fd, buffer)
// 	if err != nil {
// 		return OSError(1, err.Error(), path)
// 	}
// 	return base64.StdEncoding.EncodeToString(buffer)

// }

// func (fuser *Fuse) Write(path string, buf string, offset int, fd int) interface{} {
// 	//  fuse.write()
// 	// file := Fd2File(fd)
// 	syscall.Seek(fd, int64(offset), os.SEEK_SET)
// 	decodedBytes, _ := base64.StdEncoding.DecodeString(buf)
// 	count, err := syscall.Write(fd, decodedBytes)
// 	if err != nil {
// 		return OSError(1, err.Error(), path)
// 	}
// 	return count
// }

func (fuser *Fuse) Truncate(path string, length int, fd int) interface{} {
	//  fuse.truncate()
	full_path := Full_path(path)
	err := syscall.Truncate(full_path, int64(length))
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	return true
}

func main_test() {
	fuser := &Fuse{}
	// fuser.Access("fuse.go", 0)
	// fuser.Chmod("fuse.go", 0644)
	// fuser.Chown("fuse.go", 197108, 197108)
	attr := fuser.Getattr("/tmp/", 0)
	log.Printf("attr: %+v", attr)

	links := fuser.Readlink("/etc/os-release")
	log.Printf("attr: %+v", links)

	_ = fuser.Mkdir("/tmp/test/", 0777)
	// defer Rmdir("/tmp/test/")

	root := fuser.Statfs("/tmp")
	root, _ = json.Marshal(root)
	log.Printf("root: %s", root)

	_ = fuser.Symlink("/tmp/test/test1", "/tmp/wick_bash_autocomplete.sh")
	_ = fuser.Link("/tmp/wick_bash_autocomplete.sh", "/tmp/test/hardlink")
	// defer Unlink("/tmp/test/hardlink")

	_ = fuser.Rename("/tmp/test/test1", "/tmp/test/test")
	// defer Unlink("/tmp/test/test")
	dirs := fuser.Readdir("/tmp/test/", 0)
	log.Printf("/tmp/test: %+v", dirs)

	_ = fuser.Utimens("/tmp/ttyd.x86_64-1.7.4", []float64{1701849604.8191702, 1701849604.8191702})
	fd := fuser.Open("/tmp/test/test", 2).(int)
	defer fuser.Release("/tmp/test/test", fd)

	txt := fuser.Read("", 200, 0, fd).(string)
	decodedBytes, _ := base64.StdEncoding.DecodeString(txt)
	log.Printf("Read: %s", decodedBytes)
	fuser.Write("/tmp/test/test", txt, 50, fd)
	fuser.Flush("/tmp/test/test", fd)
	// Truncate("/tmp/test/test", 250, fd)

}
